﻿#define RENDERTOGRAPHICS

using System;
using System.Collections.Generic;
using WiMo.Games.Drawables;
using System.Drawing;
using DirectDrawWrapper;
using d3d = Microsoft.WindowsMobile.DirectX.Direct3D;

using System.Drawing.Imaging;

namespace WiMo.Games.DirectDrawDisplay
{
    public class DisplayManager : IDisplay
    {
        private List<IDrawable> _items;
        private List<IDrawable> _temporaryObjects;
        private Size _bounds;
        private Size _originalBounds;
        private IntPtr _hwnd;
        private bool _isSceneDirty = true;

        public int DPI { get; set; }
        public byte AspectRatio { get; set; }

        public Size TargetSize { get; private set; }
        public bool Diagnostics { get; set; }
        public Color BackgroundColor { get; set; }
        public ITexture BackgroundImage { get; set; }

        public Vector2 ScaleFactor { get; private set; }
        public Location DisplayOffset { get; private set; }
        public DisplayStrategies DisplayStrategy { get; set; }
        public DirectDrawGraphics DirectDrawGraphics { get; set; }

#if(RENDERTOGRAPHICS)
        private Graphics _graphics;
        private IntPtr _ddDc;
#endif

        private void SetDPI()
        {
            var bitmap = new Bitmap(64, 64);
            var gr = Graphics.FromImage(bitmap);
            DPI = Convert.ToInt32(gr.DpiX);
            gr.Dispose();
            bitmap.Dispose();
        }

        public DisplayManager(IntPtr hwnd, Size bounds)
        {
            SetDPI();
            _bounds = bounds;
            _originalBounds = bounds;
            _hwnd = hwnd;
            BackgroundColor = Color.Black;
            AspectRatio = WiMo.Games.Display.AspetRatioAny;
            TargetSize = bounds;
            ScaleFactor = new Vector2(1, 1);

            Init();
        }

        public DisplayManager(IntPtr hwnd, Size targetSize, Size bounds)
        {
            SetDPI();
            _bounds = bounds;
            _originalBounds = bounds;
            _hwnd = hwnd;
            BackgroundColor = Color.Black;
            AspectRatio = WiMo.Games.Display.AspetRatioAny;
            TargetSize = targetSize;

            ScaleFactor = new Vector2(bounds.Width / targetSize.Width, bounds.Height / targetSize.Height);

            Init();
        }


        public DisplayManager(IntPtr hwnd, Size targetSize, Size bounds, byte aspectRatio)
        {
            SetDPI();
            _bounds = bounds;
            _originalBounds = bounds;
            _hwnd = hwnd;
            BackgroundColor = Color.Black;
            AspectRatio = aspectRatio;
            TargetSize = targetSize;
            var widthRatio = bounds.Width / targetSize.Width;
            var heightRatio = bounds.Height / targetSize.Height;

            switch (aspectRatio)
            {
                case WiMo.Games.Display.AspetRatioOneByOne:
                case WiMo.Games.Display.AspetRatioFourByThree:
                case WiMo.Games.Display.AspetRatioThreeByFour:
                    var ratio = Math.Min(heightRatio, widthRatio);
                    ScaleFactor = new Vector2(ratio, ratio);

                    if (widthRatio != heightRatio)
                    {
                        if (widthRatio < heightRatio)
                            DisplayOffset = new Location(0, (bounds.Height - (targetSize.Height * ratio)) / 2);
                        else
                            DisplayOffset = new Location((bounds.Width - (targetSize.Width * ratio)) / 2, 0);
                    }
                    break;
                default:
                    ScaleFactor = new Vector2(widthRatio, heightRatio);
                    break;
            }

            Init();
        }

        private void Init()
        {
            System.Diagnostics.Debug.WriteLine("Direct Draw Driver: " + Name);
            DisplayStrategy = DisplayStrategies.Automatic;
            DirectDrawGraphics = new DirectDrawGraphics(_hwnd, BackbufferMode.Any);
            DirectDrawWrapper.DirectDrawGraphics.Init();
            WiMo.Games.Quadrant.Init(TargetSize);
        }

        public void DisableFullScreen()
        {
            System.Diagnostics.Debug.WriteLine("DISABLE FULL SCREEN MODE");
            DirectDrawGraphics.SetNormalCoopMode();
        }

        public void Resize(Size bounds)
        {
            _bounds = bounds;

            DirectDrawGraphics.Resize(bounds.Width, bounds.Height);
        }

        public void Reset()
        {
            if (_temporaryObjects != null)
            {
                foreach (var tempObj in _temporaryObjects)
                {
                    WiMo.Games.Quadrant.Root.Remove(tempObj);
                    tempObj.Dispose();
                }
            }

            _items = new List<IDrawable>();
            _temporaryObjects = new List<IDrawable>();

#if(RENDERTOGRAPHICS)
            if (!IsOriginalSize)
            {
                _graphics = null;
                _ddDc = IntPtr.Zero;
            }
            else
            {
                _ddDc = new IntPtr();
                if (DirectDrawGraphics.ScreenSurface.GetDC(ref _ddDc) != 0xff)
                    _graphics = Graphics.FromHdc(_ddDc);
                else
                    _graphics = null;
            }
#endif
        }

        private void RenderQuadTree(Quadrant quad)
        {
            if (quad.HasChildren)
            {
                RenderQuadTree(quad.NW);
                RenderQuadTree(quad.NE);
                RenderQuadTree(quad.SW);
                RenderQuadTree(quad.SE);
            }

            //var x = Convert.ToInt32(quad.BoundingRect.Left + DisplayOffset.Point.X);
            //var y = Convert.ToInt32(quad.BoundingRect.Top + DisplayOffset.Point.Y);
            //var w = Convert.ToInt32(quad.BoundingRect.Width);
            //var h = Convert.ToInt32(quad.BoundingRect.Height);

            var x = Convert.ToInt32(quad.BoundingRect.Left * ScaleFactor.X + DisplayOffset.X);
            var y = Convert.ToInt32(quad.BoundingRect.Top * ScaleFactor.Y + DisplayOffset.Y);
            var w = Convert.ToInt32(quad.BoundingRect.Width * ScaleFactor.X);
            var h = Convert.ToInt32(quad.BoundingRect.Height * ScaleFactor.Y);

            //if(quad.IsClean)
            //    _graphics.DrawRectangle(whitePen, x, y, w, h);
            //else
            //    _graphics.FillRectangle(sb, x, y, w, h);

            if (!quad.IsClean)
            {
                var whitePen = new Pen(Color.Silver.ToDrawingColor());
                var sb = new SolidBrush(Color.Black.ToDrawingColor());
                _graphics.DrawRectangle(whitePen, x, y, w, h);

                _graphics.FillRectangle(sb, x, y, w, h);

                foreach (var sprite in quad.Sprites)
                {
                    if (_items.Contains(sprite)) 
                        sprite.Render(this);
                }

                sb.Dispose();
                whitePen.Dispose();
            }
        }

        private void RenderBackground()
        {
            if (BackgroundImage != null)
                _graphics.DrawImage(BackgroundImage.TextureInformation as Image, 0, 0);
            else
            {
                DirectDrawGraphics.ScreenSurface.Fill(BackgroundColor.ToDrawingColor());
                var bgBrush = new SolidBrush(BackgroundColor.ToDrawingColor());
                _graphics.FillRectangle(bgBrush, 0, 0, _bounds.Width, _bounds.Height);
                bgBrush.Dispose();
            }

        }

        public void Render()
        {
            if (!IsOriginalSize)
            {
                System.Diagnostics.Debug.WriteLine("BOUNDS CHANGED, WILL NOT RENDER!");
#if(RENDERTOGRAPHICS)
                if (_graphics != null)
                    _graphics.Dispose();

                if (_ddDc != IntPtr.Zero)
                    DirectDrawGraphics.ScreenSurface.RelaseDC(_ddDc);
#endif
            }
            else
            {
#if(RENDERTOGRAPHICS)
                if (_graphics != null && DirectDrawGraphics.ScreenSurface != null)
                {
#endif
                    var timer = System.Diagnostics.Stopwatch.StartNew();
                    timer.Start();

                    RenderBackground();

                    var fillTime = timer.ElapsedMilliseconds;

                    timer.Reset();
                    timer.Start();

                    RenderQuadTree(WiMo.Games.Quadrant.Root);

                    foreach (var drawable in _items)
                        drawable.Render(this);

                    var listTime = timer.ElapsedMilliseconds;

                    timer.Reset();
                    timer.Start();
                    

#if(RENDERTOGRAPHICS)
                    if (_graphics != null)
                        _graphics.Dispose();

                    if (_ddDc != IntPtr.Zero)
                        DirectDrawGraphics.ScreenSurface.RelaseDC(_ddDc);
#endif

                    var resetGraphicsTime = timer.ElapsedMilliseconds;

                    timer.Reset();
                    timer.Start();

                    DirectDrawGraphics.Flip();
                    var flipTime = timer.ElapsedMilliseconds;

                    WiMo.Games.Quadrant.Root.MarkClean();
                     System.Diagnostics.Debug.WriteLine(string.Format("ALL {0:000} FILL {1:00} LIST {2:00} FLIP {3:00} RESET {4:00}", resetGraphicsTime + fillTime + listTime + flipTime,  fillTime, listTime, flipTime, resetGraphicsTime));

#if(RENDERTOGRAPHICS)
                }
#endif

            }
        }

        public void Add(WiMo.Games.Drawables.IDrawable drawable)
        {
            //It does not live in a quad, it may have moved, so re-add
            if (drawable.Quandrants.Count == 0)
                WiMo.Games.Quadrant.Root.AddChild(drawable);

            _items.Add(drawable);
        }

        public string Name
        {
            get
            {
                string driverName;
                var al = d3d.Manager.Adapters;
                string driver = al.Default.Information.DriverName.ToString();

                switch (driver)
                {
                    case "d3dmref.dll":
                        driverName = "Software Ref Drvr";
                        break;
                    case "D3DM TI3D Driver":
                        driverName = "OMAP Driver";
                        break;
                    case "PowerVR D3DMobile HAL":
                        driverName = "PowerVR GPU";
                        break;
                    case "D3DMXSC50PB.DLL":
                        driverName = "XScale Driver";
                        break;
                    default:
                        driverName = driver;
                        break;
                }

                return driverName;
            }
        }

        #region Public Draw Methods
        public ISpriteLine DrawLine(Location start, Location end, Color color, int width)
        {
            var tempLine = new Drawables.Line();
            tempLine.Scale = ScaleFactor;
            tempLine.Start = start;
            tempLine.End = end;
            tempLine.LineColor = color;
            tempLine.Width = width;
            tempLine.IsTransient = true;

            _temporaryObjects.Add(tempLine);
            _items.Add(tempLine);
            WiMo.Games.Quadrant.Root.AddChild(tempLine);

            return tempLine;
        }

        public ISpriteShape DrawRectangle(Rectangle rect, Color color, int width)
        {
            return DrawRectangle(rect, color, width, Origin.Center);
        }

        public ISpriteShape DrawRectangle(Rectangle rect, Color color, int width, Origin origin)
        {
            var tempRect = new Drawables.Rectangle();
            tempRect.Origin = origin;
            tempRect.Scale = ScaleFactor;
            tempRect.Rect = rect;
            tempRect.BorderWidth = width;
            tempRect.FillColor = Color.Transparent;
            tempRect.BorderColor = color;
            tempRect.IsTransient = true;

            _temporaryObjects.Add(tempRect);
            _items.Add(tempRect);
            WiMo.Games.Quadrant.Root.AddChild(tempRect);

            return tempRect;
        }

        public ISpriteShape FillRectangle(Rectangle rect, Color fillColor, Color borderColor, int width)
        {
            return FillRectangle(rect, fillColor, borderColor, width, Origin.Center);
        }

        public ISpriteShape FillRectangle(Rectangle rect, Color fillColor, Color borderColor, int width, Origin origin)
        {
            var tempRect = new Drawables.Rectangle();
            tempRect.Origin = origin;
            tempRect.Scale = ScaleFactor;
            tempRect.Rect = rect;
            tempRect.BorderColor = borderColor;
            tempRect.FillColor = fillColor;
            tempRect.BorderWidth = width;
            tempRect.IsTransient = true;

            _temporaryObjects.Add(tempRect);
            _items.Add(tempRect);
            WiMo.Games.Quadrant.Root.AddChild(tempRect);

            return tempRect;
        }

        public ISpriteText DrawText(PointF position, string message, ISpriteFont font, Color foreColor)
        {
            return DrawText(position, message, font, foreColor, Origin.TopLeft);
        }

        public ISpriteText DrawText(PointF position, string message, ISpriteFont font, Color foreColor, Origin origin)
        {
            var text = new Drawables.SpriteText();
            text.Origin = origin;
            text.Scale = ScaleFactor;
            text.Position = position.Clone();
            text.ForeColor = foreColor;
            text.Font = font;
            text.Text = message;
            text.IsTransient = true;

            _temporaryObjects.Add(text);
            _items.Add(text);
            WiMo.Games.Quadrant.Root.AddChild(text);

            return text;
        }

        public ISpriteShape DrawCircle(PointF position, int radius, Color borderColor, int width)
        {
            var circle = new Drawables.Ellipse();
            circle.Scale = ScaleFactor;
            circle.Position = position.Clone();
            circle.BorderWidth = width;
            circle.BorderColor = borderColor;
            circle.IsTransient = true;
            circle.Size = new Size(radius * 2, radius * 2);
            circle.FillColor = Color.Transparent;
            _temporaryObjects.Add(circle);
            _items.Add(circle);
            WiMo.Games.Quadrant.Root.AddChild(circle);

            return circle;
        }

        public ISpriteShape FillCircle(PointF position, int radius, Color fillColor, Color borderColor, int width)
        {
            var circle = new Drawables.Ellipse();
            circle.Scale = ScaleFactor;
            circle.Position = position.Clone();
            circle.Radius = radius;
            circle.BorderWidth = width;
            circle.FillColor = fillColor;
            circle.BorderColor = borderColor;
            circle.IsTransient = true;
            circle.Size = new Size(radius * 2, radius * 2);

            _temporaryObjects.Add(circle);
            _items.Add(circle);
            WiMo.Games.Quadrant.Root.AddChild(circle);

            return circle;
        }
        #endregion

        private Point GetAdjustedPosition(IPositionable shape)
        {
            int left = shape.Position.Point.X;
            int top = shape.Position.Point.Y;
            if (shape.Origin == Origin.Center)
            {
                left = Convert.ToInt32(left - shape.Size.Width / 2);
                top = Convert.ToInt32(top - shape.Size.Height / 2);
            }

            left += DisplayOffset.X;
            top += DisplayOffset.Y;

            return new Point(left, top);
        }

        private tagRECT GetDestTagRect(IPositionable shape)
        {
            var position = GetAdjustedPosition(shape);
            return new tagRECT(position.X, position.Y, position.X + shape.Size.Width, position.Y + shape.Size.Height);
        }

        private tagRECT GetSrcTagRect(IPositionable shape)
        {
            return new tagRECT(0, 0, shape.Size.Width, shape.Size.Height);
        }

#if(RENDERTOGRAPHICS)
        public void Render(ISpriteImage sprite)
        {
            if (!IsOriginalSize)
                return;

            if (!sprite.IsVisible)
                return;

            if (sprite.TextureList == null)
                throw new NullReferenceException("No associated bitmap.");

            System.Drawing.Rectangle destRect;

            var position = GetAdjustedPosition(sprite);

            destRect = new System.Drawing.Rectangle(position.X, position.Y, sprite.Size.Width, sprite.Size.Height);

            if (sprite.ColorKey != Color.Transparent)
            {
                var attr = new ImageAttributes();
                attr.SetColorKey(sprite.ColorKey.ToDrawingColor(), sprite.ColorKey.ToDrawingColor());

                _graphics.DrawImage(sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap, destRect, 0, 0, sprite.Size.Width, sprite.Size.Height, GraphicsUnit.Pixel, attr);
            }
            else
            {
                _graphics.DrawImage(sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap, destRect.Left, destRect.Top);
            }
        }
#endif

#if(!RENDERTOGRAPHICS)
        public void Render(ISpriteImage sprite)
        {
            if (!IsOriginalSize)
                return;

            if (!sprite.IsVisible)
                return;

            if (sprite.TextureList == null)
                throw new NullReferenceException("No associated bitmap.");

            var surface = sprite.DrawInformation as Surface;
            if (surface == null)
            {
                sprite.DrawInformation = DirectDrawGraphics.CreateSurfaceFromImage(sprite.TextureList[sprite.ImageIndex].TextureInformation as Bitmap);
                surface = (Surface)sprite.DrawInformation;
                if (sprite.ColorKey != Color.Transparent)
                    surface.SetColorKey(sprite.ColorKey.ToDrawingColor(), ColorKeyFlags.SRCBLT);
            }

            DirectDrawWrapper.tagRECT destRect;

            switch (sprite.Origin)
            {
                case Origin.Center:
                    int left = Convert.ToInt32(sprite.Position.X - sprite.Size.Width / 2);
                    int top = Convert.ToInt32(sprite.Position.Y - sprite.Size.Height / 2);
                    destRect = new tagRECT(left, top, sprite.Position.Point.X + sprite.Size.Width, sprite.Position.Point.Y + sprite.Size.Height);

                    break;
                default:
                    destRect = new tagRECT(sprite.Position.Point.X, sprite.Position.Point.Y, sprite.Position.Point.X + sprite.Size.Width, sprite.Position.Point.Y + sprite.Size.Height);
                    break;
            }

            var srcRef = new tagRECT(0, 0, sprite.Size.Width, sprite.Size.Height);

            if (sprite.ColorKey == Color.Transparent)
            {
                var ticks = System.Environment.TickCount;
                DirectDrawGraphics.ScreenSurface.Draw(ref destRect, surface, ref srcRef, BltFlags.DDBLT_KEYDESTOVERRIDE);
                System.Diagnostics.Debug.WriteLine("Time to render w/o color key " + (System.Environment.TickCount - ticks));
            }
            else
            {
                var ticks = System.Environment.TickCount;
                DirectDrawGraphics.ScreenSurface.Draw(ref destRect, surface, ref srcRef, BltFlags.DDBLT_KEYSRC);
                System.Diagnostics.Debug.WriteLine("Time to render w/ color key " + (System.Environment.TickCount - ticks));                
            }
        }
#endif

        public void Render(ISpriteShape shape)
        {
            if (!IsOriginalSize)
                return;

            if (shape.IsTransient)
            {
                RenderTemporaryObject(shape);
                return;
            }

            if (shape.DrawInformation == null)
            {
                switch (shape.ShapeType)
                {
                    case ShapeType.Rectangle:
                        shape.DrawInformation = BuildRectangleSurface(shape);
                        break;

                    case ShapeType.Polygon:
                        shape.DrawInformation = BuildPolygonSurface(shape);
                        break;

                    case ShapeType.Ellipse:
                        shape.DrawInformation = BuildEllipseSurface(shape);
                        break;

                    case ShapeType.LineList:
                        shape.DrawInformation = BuildLineListSurface(shape);
                        break;
                }
            }

            var surface = shape.DrawInformation as Surface;
            if (surface == null)
                return;

            var destRect = GetDestTagRect(shape);
            var srcRef = GetSrcTagRect(shape);

            DirectDrawGraphics.ScreenSurface.Draw(ref destRect, surface, ref srcRef, BltFlags.DDBLT_KEYSRC);
        }

        private void RenderTemporaryObject(ISpriteShape shape)
        {
            if (!IsOriginalSize)
                return;

#if(!RENDERTOGRAPHICS)
            var dc = new IntPtr();
            DirectDrawGraphics.ScreenSurface.GetDC(ref dc);
            var gr = Graphics.FromHdc(dc);
#else
            var gr = _graphics;
#endif
            Brush fillBrush = null;
            if (shape.Texture == null)
                fillBrush = (shape.BorderColor != Color.Transparent) ? new SolidBrush(shape.FillColor.ToDrawingColor()) : null;
            else
                fillBrush = new TextureBrush(shape.Texture.TextureInformation as Bitmap);

            Pen borderPen = (shape.BorderColor != Color.Transparent && shape.BorderWidth > 0) ? new Pen(shape.BorderColor.ToDrawingColor(), shape.BorderWidth) : null;

            switch (shape.ShapeType)
            {
                case ShapeType.Rectangle:
                    if (shape.FillColor != Color.Transparent)
                        gr.FillRectangle(fillBrush, shape.Rect.ToDrawingRect(DisplayOffset));

                    if (shape.BorderColor != Color.Transparent)
                        gr.DrawRectangle(borderPen, shape.Rect.ToDrawingRect(DisplayOffset));
                    break;

                case ShapeType.Polygon:
                    var positionedPoints = new List<Point>();

                    foreach (var point in shape.Points)
                        positionedPoints.Add(new Point(point.X + shape.Position.Point.X + DisplayOffset.X, point.Y + shape.Position.Point.Y + DisplayOffset.Y));

                    if (fillBrush != null)
                        gr.FillPolygon(fillBrush, positionedPoints.ToArray());

                    if (borderPen != null)
                        gr.DrawPolygon(borderPen, positionedPoints.ToArray());

                    break;

                case ShapeType.Ellipse:
                    if (fillBrush != null)
                        gr.FillEllipse(fillBrush, shape.Rect.ToDrawingRect(DisplayOffset));

                    if (borderPen != null)
                        gr.DrawEllipse(borderPen, shape.Rect.ToDrawingRect(DisplayOffset));
                    break;

                case ShapeType.LineList:
                    var positionedLines = new List<Point>();

                    foreach (var point in shape.Points)
                        positionedLines.Add(new Point(point.X + shape.Position.Point.X + DisplayOffset.X, point.Y + shape.Position.Point.Y + DisplayOffset.Y));

                    if (borderPen != null)
                        gr.DrawLines(borderPen, positionedLines.ToArray());

                    break;
            }

            if (fillBrush != null)
                fillBrush.Dispose();

            if (borderPen != null)
                borderPen.Dispose();

#if(!RENDERTOGRAPHICS)
            gr.Dispose();
            //DirectDrawGraphics.ScreenSurface.RelaseDC(dc);
#endif

        }

        public void Render(ISpriteLine line)
        {
            if (!IsOriginalSize)
                return;

#if(!RENDERTOGRAPHICS)
            var dc = new IntPtr();
            DirectDrawGraphics.ScreenSurface.GetDC(ref dc);
            var gr = Graphics.FromHdc(dc);
#else
            var gr = _graphics;
#endif

            var pen = new Pen(line.LineColor.ToDrawingColor(), line.Width);
            gr.DrawLine(pen, (int)(line.Start.X + DisplayOffset.X), (line.Start.Y + DisplayOffset.Y), (line.End.X + DisplayOffset.X), (line.End.Y + DisplayOffset.Y));
            pen.Dispose();

#if(!RENDERTOGRAPHICS)
            gr.Dispose();
            DirectDrawGraphics.ScreenSurface.RelaseDC(dc);
#endif
        }

        private void RenderTransientText(ISpriteText text)
        {
#if(!RENDERTOGRAPHICS)
            var dc = new IntPtr();
            DirectDrawGraphics.ScreenSurface.GetDC(ref dc);

            var localGr = Graphics.FromHdc(dc);
#else
            var localGr = _graphics;
#endif

            var size = _graphics.MeasureString(text.Text, text.Font.FontInformation as
                Font);
            var sb = new SolidBrush(text.ForeColor.ToDrawingColor());
            if (text.Origin == Origin.TopLeft)
                localGr.DrawString(text.Text, text.Font.FontInformation as Font, sb, text.Position.X + DisplayOffset.X, text.Position.Y + DisplayOffset.Y);
            else
                localGr.DrawString(text.Text, text.Font.FontInformation as Font, sb, (text.Position.X - (int)(size.Width / 2)) + DisplayOffset.X, (text.Position.Y - (int)(size.Height / 2)) + DisplayOffset.Y);

            sb.Dispose();

#if(!RENDERTOGRAPHICS)
            gr.Dispose();
            DirectDrawGraphics.ScreenSurface.RelaseDC(dc);
#endif
        }

        private void RenderSpriteText(ISpriteText text)
        {
            var textGraphics = text.DrawInformation as Bitmap;
            if (textGraphics == null)
            {
                textGraphics = new Bitmap(text.Size.Width, text.Size.Height);

                var gr = Graphics.FromImage(textGraphics);
                var solidBrush = new SolidBrush(text.BackgroundColor.ToDrawingColor());
                gr.FillRectangle(solidBrush, 0, 0, text.Size.Width, text.Size.Height);

                var textSize = gr.MeasureString(text.Text, text.Font.FontInformation as Font);

                var left = (text.Origin == Origin.TopLeft) ? 0 : (text.Size.Width - textSize.Width) / 2;
                var top = (text.Origin == Origin.TopLeft) ? 0 : (text.Size.Height - textSize.Height) / 2;

                var textBrush = new SolidBrush(text.ForeColor.ToDrawingColor());

                gr.DrawString(text.Text, text.Font.FontInformation as Font, textBrush, left, top);

                textBrush.Dispose();
                solidBrush.Dispose();

                gr.Dispose();

                text.DrawInformation = textGraphics;
            }
#if(!RENDERTOGRAPHICS)
            var dc = new IntPtr();
            DirectDrawGraphics.ScreenSurface.GetDC(ref dc);

            var localGr = Graphics.FromHdc(dc);
#else
            var localGr = _graphics;
#endif
            Point location = GetAdjustedPosition(text);
            localGr.DrawImage(textGraphics, location.X, location.Y);

#if(!RENDERTOGRAPHICS)
            gr.Dispose();
            DirectDrawGraphics.ScreenSurface.RelaseDC(dc);
#endif
        }

        private float GetScaledFontSize(float fontSize)
        {
            switch (DPI)
            {
                case 96:
                    if (ScaleFactor.X != 1.0f && ScaleFactor.Y != 1.0f)
                        return fontSize * ScaleFactor.X;
                    break;
                case 128:
                    return fontSize / 1.5f;
                case 192:
                    if (ScaleFactor.X != 2.0f && ScaleFactor.Y != 2.0f)
                        return fontSize / 1.5f;
                    break;
            }

            return fontSize;
        }

        public void Render(ISpriteText text)
        {
            if (!IsOriginalSize)
                return;

            if (text.Font.FontInformation == null)
            {
                var fontStyle = FontStyle.Regular;
                if (text.Font.Bold) fontStyle |= FontStyle.Bold;
                if (text.Font.Italics) fontStyle |= FontStyle.Italic;
                if (text.Font.Underline) fontStyle |= FontStyle.Underline;
                if (text.Font.StrikeThrough) fontStyle |= FontStyle.Strikeout;



                //text.Font.FontInformation = new Font(text.Font.Name, text.Font.Size / ScaleFactor.X, fontStyle);
                text.Font.FontInformation = new Font(text.Font.Name, GetScaledFontSize(text.Font.Size), fontStyle);
            }

            if (text.IsTransient)
                RenderTransientText(text);
            else
                RenderSpriteText(text);

        }

        private Surface BuildRectangleSurface(ISpriteShape shape)
        {
            if (shape.Size.Width == 0 || shape.Size.Height == 0)
                return null;

            var borderPen = (shape.BorderColor != Color.Transparent && shape.BorderWidth > 0) ? new Pen(shape.BorderColor.ToDrawingColor(), shape.BorderWidth) : null;
            var bitmap = new Bitmap(shape.Size.Width, shape.Size.Height);
            var gr = Graphics.FromImage(bitmap);

            Brush fillBrush = null;
            if (shape.Texture == null)
                fillBrush = (shape.FillColor != Color.Transparent) ? new SolidBrush(shape.FillColor.ToDrawingColor()) : null;
            else
                fillBrush = new TextureBrush(shape.Texture.TextureInformation as Bitmap);

            if (fillBrush != null)
            {
                gr.FillRectangle(fillBrush, 0, 0, shape.Size.Width, shape.Size.Height);
                fillBrush.Dispose();
            }

            if (borderPen != null)
            {
                gr.DrawRectangle(borderPen, 0, 0, shape.Size.Width - shape.BorderWidth, shape.Size.Height - shape.BorderWidth);
                borderPen.Dispose();
            }

            var surface = DirectDrawGraphics.CreateSurfaceFromImage(bitmap);
            if (shape.FillColor != Color.Black && shape.BorderColor != Color.Black)
                surface.SetColorKey(Color.Black.ToDrawingColor(), ColorKeyFlags.SRCBLT);
            else
                surface.SetColorKey(Color.White.ToDrawingColor(), ColorKeyFlags.SRCBLT);

            gr.Dispose();

            bitmap.Dispose();

            return surface;
        }

        private Surface BuildEllipseSurface(ISpriteShape shape)
        {
            var bitmap = new Bitmap(shape.Size.Width, shape.Size.Height);
            var gr = Graphics.FromImage(bitmap);

            Brush fillBrush = null;
            if (shape.Texture == null)
                fillBrush = (shape.FillColor != Color.Transparent) ? new SolidBrush(shape.FillColor.ToDrawingColor()) : null;
            else
                fillBrush = new TextureBrush(shape.Texture.TextureInformation as Bitmap);

            var borderPen = (shape.BorderColor != Color.Transparent && shape.BorderWidth > 0) ? new Pen(shape.BorderColor.ToDrawingColor(), shape.BorderWidth) : null;

            if (fillBrush != null)
            {
                gr.FillEllipse(fillBrush, 0, 0, shape.Size.Width - shape.BorderWidth, shape.Size.Height - shape.BorderWidth);
                fillBrush.Dispose();
            }

            if (borderPen != null)
            {
                gr.DrawEllipse(borderPen, 0, 0, shape.Size.Width - shape.BorderWidth, shape.Size.Height - shape.BorderWidth);
                borderPen.Dispose();
            }

            gr.Dispose();

            var surface = DirectDrawGraphics.CreateSurfaceFromImage(bitmap);
            surface.SetColorKey(Color.Black.ToDrawingColor(), ColorKeyFlags.SRCBLT);
            bitmap.Dispose();

            return surface;
        }

        private Point[] GetPointArray(List<Location> locations)
        {
            Point[] points = new Point[locations.Count];
            for (int i = 0; i < locations.Count; i++)
                points[i] = new Point(locations[i].X, locations[i].Y);

            return points;
        }

        private Surface BuildLineListSurface(ISpriteShape shape)
        {

            var bitmap = new Bitmap(shape.Size.Width + shape.BorderWidth, shape.Size.Height + shape.BorderWidth);
            var gr = Graphics.FromImage(bitmap);

            Pen borderPen = (shape.BorderColor != Color.Transparent && shape.BorderWidth > 0) ? new Pen(shape.BorderColor.ToDrawingColor(), shape.BorderWidth) : null;

            if (borderPen != null)
            {
                gr.DrawLines(borderPen, GetPointArray(shape.Points));
                borderPen.Dispose();
            }

            gr.Dispose();

            var surface = DirectDrawGraphics.CreateSurfaceFromImage(bitmap);
            surface.SetColorKey(Color.Black.ToDrawingColor(), ColorKeyFlags.SRCBLT);
            bitmap.Dispose();

            return surface;
        }

        private Surface BuildPolygonSurface(ISpriteShape shape)
        {
            var bitmap = new Bitmap(shape.Size.Width + 2, shape.Size.Height + 2);
            var gr = Graphics.FromImage(bitmap);

            Brush fillBrush = null;
            if (shape.Texture == null)
                fillBrush = (shape.FillColor != Color.Transparent) ? new SolidBrush(shape.FillColor.ToDrawingColor()) : null;
            else
                fillBrush = new TextureBrush(shape.Texture.TextureInformation as Bitmap);

            var borderPen = (shape.BorderColor != Color.Transparent && shape.BorderWidth > 0) ? new Pen(shape.BorderColor.ToDrawingColor(), shape.BorderWidth) : null;

            if (fillBrush != null)
            {
                gr.FillPolygon(fillBrush, GetPointArray(shape.Points));
                fillBrush.Dispose();
            }

            if (borderPen != null)
            {
                gr.DrawPolygon(borderPen, GetPointArray(shape.Points));
                borderPen.Dispose();
            }

            gr.Dispose();

            var surface = DirectDrawGraphics.CreateSurfaceFromImage(bitmap);
            surface.SetColorKey(Color.Black.ToDrawingColor(), ColorKeyFlags.SRCBLT);
            bitmap.Dispose();

            return surface;
        }

        private bool IsOriginalSize
        {
            get { return _bounds == _originalBounds; }
        }

        public Size Size
        {
            get { return _bounds; }
        }

        public void Invalidate()
        {
            _isSceneDirty = true;
        }


        public ISpriteImage CreateSpriteImage()
        {
            return new Drawables.SpriteImage();
        }

        public ISpriteShape CreateRectangle()
        {
            return new Drawables.Rectangle();
        }

        public ISpriteText CreateText()
        {
            return new Drawables.SpriteText();
        }

        public ISpriteLine CreateLine()
        {
            return new Drawables.Line();
        }

        public ISpriteShape CreateEllipse()
        {
            return new Drawables.Ellipse();
        }

        public ISpriteShape CreatePolygon()
        {
            return new Drawables.Polygon();
        }

        public ISpriteShape CreateLineList()
        {
            return new Drawables.LineList();
        }

        public void Dispose()
        {
            DirectDrawGraphics.Dispose();
        }
    }
}
